/**
 * 
 */
package com.gxa.hualian.dao.impl;

import com.gxa.hualian.dao.BaseDao;
import com.gxa.hualian.util.DBUtil;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;


/**
 * 通用的BaseDao的实现类
 * 当接口BaseDao带有泛型T的声明的时候,实现类也需要继承这一特点
 * 实现类中的<T>和接口中的<T>即为同一个类型
 * BaseDaoImpl负责提供操作模板,所以不应该被实例化,作用是被子类继承
 * @author bilei
 * @date 2022年4月15日 
 */

public  abstract class BaseDaoImpl<T> implements BaseDao<T> {

	private DBUtil dbUtil = new DBUtil();
	
	@Override
	public T selectById(Integer id,Class<T> clazz) throws Exception {
		// 创建通用的select全查询语句
		// user/manager 就是类的名字
		String select = "select * from tb_"+clazz.getSimpleName()+" where id=?";
		PreparedStatement preparedStatement = dbUtil.createPreparedStatement(select,id);
		ResultSet resultSet = dbUtil.execQuery(preparedStatement);
		
		// 声明一个T对象t,赋值为null
		T t = null;
		if(resultSet.next()) {
			//user = new User(); / manager = new Manager();
			t = clazz.getConstructor().newInstance();
			
			// 通过反射获取所有的字段列表(private)
			Field[]  fields = clazz.getDeclaredFields();
			for (int i = 0; i < fields.length; i++) {
				// 暴力获得访问权限
				fields[i].setAccessible(true);
				// 给通过反射实例化的对象t赋字段值
				if ("regTime".equals(fields[i].getName()) || "collectExp".equals(fields[i].getName())|| "usedExp".equals(fields[i].getName())){
					LocalDateTime time= (LocalDateTime) resultSet.getObject(fields[i].getName());
					Date datetime=Date.from(time.atZone(ZoneId.systemDefault()).toInstant());
					fields[i].set(t, datetime);
				}else {
					// 再通过invoke反射调用, 把数据库中对应的值反射赋值给t的对应字段
					fields[i].set(t, resultSet.getObject(fields[i].getName()));

				}

			}
		}
		return t;
	}

	@Override
	public List<T> selectByName(String name, String value, Class<T> clazz) throws Exception {
		// 创建通用的select查询语句(根据字段特征): select * from tb_name where fieldName=?
		StringBuilder builder = new StringBuilder("select * from tb_");
		builder.append(clazz.getSimpleName());
		builder.append(" where ");
		builder.append(name);
		builder.append("=?");
		PreparedStatement preparedStatement = dbUtil.createPreparedStatement(builder.toString());
		// 传参
		preparedStatement.setObject(1,value);
		ResultSet resultSet = dbUtil.execQuery(preparedStatement);

		// 声明集合
		List<T> list = new ArrayList<>();


		while(resultSet.next()) {
			T t = clazz.getConstructor().newInstance();
			Field[]  fields = clazz.getDeclaredFields();
			for (int i = 0; i < fields.length; i++) {
				// 获取字段名
				String fieldName =  fields[i].getName();
				// 把字段名的首字母变成大写
				String upper = Character.toUpperCase(fieldName.charAt(0)) + fieldName.substring(1);
				// 得到了真正的方法名 setId
				String methodName = "set"+upper    ;

				//反射获取方法对象: setXXX, 参数部分通过Field的getType方法获得准确和匹配的Class<?>
				Method method = clazz.getMethod(methodName,fields[i].getType() );
				if ("regTime".equals(fields[i].getName()) || "collectExp".equals(fields[i].getName())|| "usedExp".equals(fields[i].getName())){
					LocalDateTime time= (LocalDateTime) resultSet.getObject(fields[i].getName());
					Date datetime=Date.from(time.atZone(ZoneId.systemDefault()).toInstant());
					method.invoke(t,datetime);
				}else {
					// 再通过invoke反射调用, 把数据库中对应的值反射赋值给t的对应字段
					method.invoke(t, resultSet.getObject(fields[i].getName()));

				}

			}

			// 把当前的t对象填充到集合
			list.add(t);
		}
		dbUtil.closeAll();
		return list;
	}

	@Override
	
	public Integer insert(T t ) throws Exception {
		//INSERT INTO tb_User(id,name,phone,email,pwd,address) VALUES(NULL,?,?,?,?,?)

		Class<?> clazz = t.getClass();
		// 动态获取表名
		String className = clazz.getSimpleName();
		// 动态获取字段数量(排除id,如果有序列化操作也应该排除序列化UID
		Field[] fields = clazz.getDeclaredFields();

		// 准备一个StringBuilder
		// 约定大于配置(开发中常常用约定来形成规范)
		StringBuilder builder = new StringBuilder();
		builder.append("INSERT INTO tb_");
		builder.append(className);
		builder.append("(id");

		// 动态追加字段列表

		for (int i = 1; i < fields.length; i++) {
			builder.append(", `"+fields[i].getName());
			builder.append("`");
		}

		builder.append(")");
		builder.append(" VALUES(NULL");



		//要传值的占位符数量比字段少1(不用管id)
		for (int i = 1; i < fields.length; i++) {
			builder.append(",?");
		}

		builder.append(")");

		// 生成预编译命令对象
		PreparedStatement preparedStatement = dbUtil.createPreparedStatement(builder.toString());
		// 给参数传值过程
		System.out.println(builder.toString());
		// 再次使用遍历字段的数组
		// 注意: id不一定是第一个
		for (int i = 0; i < fields.length; i++) {
			if(!fields[i].getName().equals("id")) {
				// tb_user表的字段名和类User中的字段是匹配的
				//name , 从t中获取字段name的值
				// 获取字段的操作权限
				fields[i].setAccessible(true);

				preparedStatement.setObject(i, fields[i].get(t));
			}
		}
		int lines = dbUtil.execUpdate(preparedStatement);

		dbUtil.closeAll();

		return lines;
	}

	@Override
	public Integer update(String name,Object id, T t) throws Exception {
		//UPDATE `tb_user` SET `name`=?,`phone`=?,`email`=?,`pwd`=?,`address`=? WHERE id=?
		
		Class<?> clazz = t.getClass();
		// 动态获取表名
		String className = clazz.getSimpleName(); 
		// 动态获取字段数量(排除id,如果有序列化操作也应该排除序列化UID
		Field[] fields = clazz.getDeclaredFields();
		
		// 准备一个StringBuilder
		// 约定大于配置(开发中常常用约定来形成规范)
		StringBuilder builder = new StringBuilder("UPDATE `tb_");
		builder.append(className);
		builder.append("` SET ");
		
		for (int i = 0; i < fields.length; i++) {
			if(!fields[i].getName().equals("id")) {
				builder.append("`");
				builder.append(fields[i].getName());
				builder.append("`=?,");
			}
		}
		
		// 单独处理最后的","
		builder.deleteCharAt(builder.length()-1);
		builder.append(" WHERE "+name+"=?");

		
		// 生成预编译命令对象
		PreparedStatement preparedStatement = dbUtil.createPreparedStatement(builder.toString());
		
		// 给参数传值过程
		int i ;   //当循环完毕,最后一次给id作为?的位置
		for (i = 0; i < fields.length; i++) {
			if(!fields[i].getName().equals("id")) {
				fields[i].setAccessible(true);
				preparedStatement.setObject(i, fields[i].get(t));
			}
		}
		// 单独给id赋值
		preparedStatement.setObject(i,id);
		System.out.println(builder);
		Integer integer= dbUtil.execUpdate(preparedStatement);
		dbUtil.closeAll();
		return integer;
	}




	@Override
	public List<T> selectAll(Class<T> clazz) throws Exception {
		// 创建通用的select全查询语句: select * from tb_name
		// * 处理为字段名 : id,name,...address
		String select = "select * from tb_"+clazz.getSimpleName();
		PreparedStatement preparedStatement = dbUtil.createPreparedStatement(select);
		ResultSet resultSet = dbUtil.execQuery(preparedStatement);
		
		// 声明集合
		List<T> list = new ArrayList<>();
		
		// selectAll/ selectLikeName 应该用集合操作
		while(resultSet.next()) {
			// 声明一个T对象t,赋值为null
			T t = clazz.getConstructor().newInstance();
			
			// 通过反射获取所有的字段列表(private)
			//id,name-> setId(),setName()
			Field[]  fields = clazz.getDeclaredFields();
			for (int i = 0; i < fields.length; i++) {
				// 获取字段名
				String fieldName =  fields[i].getName(); // id
				// 把字段名的首字母变成大写
				String upper = Character.toUpperCase(fieldName.charAt(0)) + fieldName.substring(1);
				;
				// 得到了真正的方法名
				String methodName = "set"+upper    ;           //setId
				
				//反射获取方法对象: setXXX, 参数部分通过Field的getType方法获得准确和匹配的Class<?>
				Method method = clazz.getMethod(methodName,fields[i].getType() );
				// 再通过invoke反射调用, 把数据库中对应的值反射赋值给t的对应字段
				if ("regTime".equals(fields[i].getName()) || "collectExp".equals(fields[i].getName())|| "usedExp".equals(fields[i].getName())){
					LocalDateTime time= (LocalDateTime) resultSet.getObject(fields[i].getName());
					Date datetime=Date.from(time.atZone(ZoneId.systemDefault()).toInstant());
					method.invoke(t,datetime);
				}else {
					// 再通过invoke反射调用, 把数据库中对应的值反射赋值给t的对应字段
					method.invoke(t, resultSet.getObject(fields[i].getName()));

				}
			}
			
			// 把当前的t对象填充到集合
			list.add(t);
		}
		dbUtil.closeAll();
		return list;
	}
	/**
	 * 删除操作
	 */
	@Override
	public Boolean deleteByName(String name,Object vlaue, Class<T> clazz) throws Exception {
		String simpleName=clazz.getSimpleName();
		StringBuilder builder = new StringBuilder("delete from tb_");
		builder.append(simpleName);
		builder.append(" where ");
		builder.append(name);
		builder.append("=");
		builder.append("?");
		PreparedStatement preparedStatement = dbUtil.createPreparedStatement(builder.toString(), vlaue);
		Integer integer=dbUtil.execUpdate(preparedStatement);
		dbUtil.closeAll();
		return  integer>0;
	}




	@Override
	public List<T> selectByPage(Integer page, Integer limit, Class<T> clazz) throws Exception {
		// 创建通用的select查询语句(根据字段特征): select * from tb_name where fieldName=?
		StringBuilder builder = new StringBuilder("select * from tb_");
		builder.append(clazz.getSimpleName());
		builder.append(" ORDER BY id asc");
		builder.append(" LIMIT ");

		builder.append("?,?");

		PreparedStatement preparedStatement = dbUtil.createPreparedStatement(builder.toString());
		// 传参

		preparedStatement.setObject(1,(page-1)*limit);
		//指定每页记录数
		preparedStatement.setObject(2,limit);
		ResultSet resultSet = dbUtil.execQuery(preparedStatement);

		// 声明集合
		List<T> list = new ArrayList<>();


		while(resultSet.next()) {
			T t = clazz.getConstructor().newInstance();
			Field[]  fields = clazz.getDeclaredFields();
			for (int i = 0; i < fields.length; i++) {
				// 获取字段名
				String fieldName =  fields[i].getName();
				// 把字段名的首字母变成大写
				String upper = Character.toUpperCase(fieldName.charAt(0)) + fieldName.substring(1);
				// 得到了真正的方法名 setId
				String methodName = "set"+upper    ;

				//反射获取方法对象: setXXX, 参数部分通过Field的getType方法获得准确和匹配的Class<?>
				Method method = clazz.getMethod(methodName,fields[i].getType() );
				if ("regTime".equals(fields[i].getName()) || "collectExp".equals(fields[i].getName())|| "usedExp".equals(fields[i].getName())){
					LocalDateTime time= (LocalDateTime) resultSet.getObject(fields[i].getName());
					Date datetime=Date.from(time.atZone(ZoneId.systemDefault()).toInstant());
					method.invoke(t,datetime);
				}else {
					// 再通过invoke反射调用, 把数据库中对应的值反射赋值给t的对应字段
					method.invoke(t, resultSet.getObject(fields[i].getName()));

				}

			}

			// 把当前的t对象填充到集合
			list.add(t);
		}
		dbUtil.closeAll();
		return list;
	}
	@Override
	public List<T> selectLikeName(Object value, Class<T> clazz) throws Exception {
		//创建通用的selectLikeName查询语句

		String select="select * from tb_"+clazz.getSimpleName()+" where phone like '%"+value+"%'";
		//预编译对象
		PreparedStatement preparedStatement=dbUtil.createPreparedStatement(select);
		//获得结果集
		ResultSet resultSet = dbUtil.execQuery(preparedStatement);
		//声明一个集合
		List<T> list=new ArrayList<>();
		//结果集游标滚动
		while (resultSet.next()){
			//通过反射获得所有的字段列表
			Field[] fields=clazz.getDeclaredFields();
			//实例化对象t

			T t=clazz.getConstructor().newInstance();
			for (int i=0;i<fields.length;i++){
				// 获取字段名
				String fieldName=fields[i].getName();//id
				//把字段首字母变成大写
				String upper=Character.toUpperCase(fieldName.charAt(0))+fieldName.substring(1);
				//得到真正的方法名
				String methodName="set"+upper; //setId
				//反射获取方法对象:setName,参数部分通过Field的getType方法获得准确和匹配的Class<?>
				Method method= clazz.getMethod(methodName,fields[i].getType());
				//再通过invoke反射调用,把数据库中对应的数据传递到对象t
				if ("regTime".equals(fields[i].getName()) || "collectExp".equals(fields[i].getName())|| "usedExp".equals(fields[i].getName())){
					LocalDateTime time= (LocalDateTime) resultSet.getObject(fields[i].getName());
					Date datetime=Date.from(time.atZone(ZoneId.systemDefault()).toInstant());
					method.invoke(t,datetime);
				}else {
					// 再通过invoke反射调用, 把数据库中对应的值反射赋值给t的对应字段
					method.invoke(t, resultSet.getObject(fields[i].getName()));

				}

			}
			//把当前的t对象填充到集合
			list.add(t);
		}
		dbUtil.closeAll();
		return list;
	}



	@Override
	public T selectByPhone(String phone, Class<T> clazz) throws Exception {
		// 创建通用的select全查询语句
		// user/manager 就是类的名字
		String select = "select * from tb_"+clazz.getSimpleName()+" where phone=?";
		PreparedStatement preparedStatement = dbUtil.createPreparedStatement(select,phone);
		ResultSet resultSet = dbUtil.execQuery(preparedStatement);

		// 声明一个T对象t,赋值为null
		T t = null;
		if(resultSet.next()) {
			//user = new User(); / manager = new Manager();
			t = clazz.getConstructor().newInstance();

			// 通过反射获取所有的字段列表(private)
			Field[]  fields = clazz.getDeclaredFields();
			for (int i = 0; i < fields.length; i++) {
				// 暴力获得访问权限
				fields[i].setAccessible(true);
				// 给通过反射实例化的对象t赋字段值
				if ("regTime".equals(fields[i].getName()) || "collectExp".equals(fields[i].getName())|| "usedExp".equals(fields[i].getName())){
					LocalDateTime time= (LocalDateTime) resultSet.getObject(fields[i].getName());
					Date datetime=Date.from(time.atZone(ZoneId.systemDefault()).toInstant());
					fields[i].set(t, datetime);
				}else {
					// 再通过invoke反射调用, 把数据库中对应的值反射赋值给t的对应字段
					fields[i].set(t, resultSet.getObject(fields[i].getName()));

				}

			}
		}
		return t;
	}
	@Override
	public List<T> selectByQuery(String queryString, Class<T> clazz, Object... args) throws Exception {
		//语句通过参数传参

		PreparedStatement preparedStatement = dbUtil.createPreparedStatement(queryString);
		ResultSet resultSet = dbUtil.execQuery(preparedStatement);
//		for (int i = 0; i <args.length ; i++) {
//			preparedStatement.setObject(i+1,args);
//		}
		// 声明集合
		List<T> list = new ArrayList<>();

		// selectAll/ selectLikeName 应该用集合操作
		while(resultSet.next()) {
			// 声明一个T对象t,赋值为null
			T t = clazz.getConstructor().newInstance();

			// 通过反射获取所有的字段列表(private)
			//id,name-> setId(),setName()
			Field[]  fields = clazz.getDeclaredFields();
			for (int i = 0; i < fields.length; i++) {
				// 获取字段名
				String fieldName =  fields[i].getName(); // id
				// 把字段名的首字母变成大写
				String upper = Character.toUpperCase(fieldName.charAt(0)) + fieldName.substring(1);
				;
				// 得到了真正的方法名
				String methodName = "set"+upper    ;           //setId

				//反射获取方法对象: setXXX, 参数部分通过Field的getType方法获得准确和匹配的Class<?>
				Method method = clazz.getMethod(methodName,fields[i].getType() );
				// 再通过invoke反射调用, 把数据库中对应的值反射赋值给t的对应字段
				if ("regTime".equals(fields[i].getName()) || "collectExp".equals(fields[i].getName())|| "usedExp".equals(fields[i].getName())){
					LocalDateTime time= (LocalDateTime) resultSet.getObject(fields[i].getName());
					Date datetime=Date.from(time.atZone(ZoneId.systemDefault()).toInstant());
					method.invoke(t,datetime);
				}else {
					// 再通过invoke反射调用, 把数据库中对应的值反射赋值给t的对应字段
					method.invoke(t, resultSet.getObject(fields[i].getName()));

				}
			}

			// 把当前的t对象填充到集合
			list.add(t);
		}
		dbUtil.closeAll();
		return list;
	}
}


